package city.spring.modules.system.controller;

import city.spring.modules.system.entity.MenuEntity;
import city.spring.modules.system.entity.PermissionEntity;
import city.spring.modules.system.entity.RoleEntity;
import city.spring.modules.system.service.MenuService;
import city.spring.modules.system.service.PermissionService;
import city.spring.modules.system.service.RoleService;
import city.spring.utils.MyBatisUtils;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.api.ApiController;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.commons.lang.StringUtils;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;

/**
 * Controller：菜单信息
 *
 * @author 侯坤林
 * @date 2020-04-12 15:33:27
 */
@RestController
@RequestMapping("/menu")
public class MenuController extends ApiController {
    private final MenuService menuService;
    private final RoleService roleService;
    private final PermissionService permissionService;
    /**
     * 实体类可排序字段
     */
    private final List<SFunction<MenuEntity, ?>> entityOrderFields;

    public MenuController(MenuService menuService, RoleService roleService, PermissionService permissionService) {
        this.menuService = menuService;
        this.roleService = roleService;
        this.permissionService = permissionService;
        entityOrderFields = new ArrayList<>();
        entityOrderFields.add(MenuEntity::getId);
        entityOrderFields.add(MenuEntity::getPid);
        entityOrderFields.add(MenuEntity::getTitle);
        entityOrderFields.add(MenuEntity::getPath);
        entityOrderFields.add(MenuEntity::getRedirect);
        entityOrderFields.add(MenuEntity::getType);
        entityOrderFields.add(MenuEntity::getComponentName);
        entityOrderFields.add(MenuEntity::getComponentPath);
        entityOrderFields.add(MenuEntity::getIcon);
        entityOrderFields.add(MenuEntity::getIsShow);
        entityOrderFields.add(MenuEntity::getIsShowChildren);
        entityOrderFields.add(MenuEntity::getIsKeepAlive);
        entityOrderFields.add(MenuEntity::getSorted);
    }

    /**
     * 获取全部的 <strong>多级分类信息</strong> 列表
     */
    @PreAuthorize("hasAnyRole('ADMIN', 'MENU') or hasAnyAuthority('menu.list')")
    @GetMapping("tree")
    public Object listTree(@PageableDefault(sort = {"sorted"}) Pageable pageable, MenuEntity entity) {
        LambdaQueryChainWrapper<MenuEntity> lambdaQuery = buildLambdaQuery(entity);
        MyBatisUtils.lambdaQueryAddOrder(lambdaQuery, pageable, entityOrderFields);
        List<MenuEntity> list = lambdaQuery.list();

        return success(menuService.converterToTreeDatasource(list));
    }


    /**
     * 以 父级ID 获取全部的 <strong>菜单信息</strong> 列表
     */
    @PreAuthorize("hasAnyRole('ADMIN', 'MENU') or hasAnyAuthority('menu.list')")
    @GetMapping("list-pid-{pid}")
    public Object listPid(@PathVariable(required = false) String pid, @PageableDefault(sort = {"sorted"}) Pageable pageable, MenuEntity entity) {
        LambdaQueryChainWrapper<MenuEntity> lambdaQuery = buildLambdaQuery(entity);
        MyBatisUtils.lambdaQueryAddOrder(lambdaQuery, pageable, entityOrderFields);
        // 搜索条件是否为空
        boolean emptyOfWhere = lambdaQuery.getWrapper().isEmptyOfWhere();
        if (StringUtils.isNotBlank(pid)) {
            lambdaQuery.eq(emptyOfWhere, MenuEntity::getPid, pid);
        } else {
            // pid 为 null 或者 空字符串
            lambdaQuery.eq(emptyOfWhere, MenuEntity::getPid, "")
                    .or()
                    .isNull(MenuEntity::getPid);
        }
        List<MenuEntity> list = lambdaQuery.list();

        // 这里处理树形结构数据的时候，不自动加载父级信息，因为会与前端已有的数据冲突
        return success(menuService.converterToTreeDatasource(list, false));
    }

    /**
     * 获取全部的 <strong>菜单信息</strong> 列表
     */
    @PreAuthorize("hasAnyRole('ADMIN', 'MENU') or hasAnyAuthority('menu.list')")
    @GetMapping("all")
    public Object listAll(@PageableDefault Pageable pageable, MenuEntity entity) {
        LambdaQueryChainWrapper<MenuEntity> lambdaQuery = buildLambdaQuery(entity);
        MyBatisUtils.lambdaQueryAddOrder(lambdaQuery, pageable, entityOrderFields);
        return success(lambdaQuery.list());
    }

    /**
     * 分页获取 <strong>菜单信息</strong> 列表
     *
     * @param pageable 分页参数信息
     */
    @PreAuthorize("hasAnyRole('ADMIN', 'MENU') or hasAnyAuthority('menu.list')")
    @GetMapping
    public Object list(@PageableDefault Pageable pageable, MenuEntity entity) {
        LambdaQueryChainWrapper<MenuEntity> lambdaQuery = buildLambdaQuery(entity);
        MyBatisUtils.lambdaQueryAddOrder(lambdaQuery, pageable, entityOrderFields);
        Page<MenuEntity> page = lambdaQuery.page(MyBatisUtils.toPage(pageable, false));
        return success(page);
    }

    /**
     * 获取一个 <strong>菜单信息</strong>
     *
     * @param id 主键ID
     */
    @PreAuthorize("hasAnyRole('ADMIN', 'MENU') or hasAnyAuthority('menu.info')")
    @GetMapping("{id}")
    public Object info(@PathVariable String id) {
        MenuEntity entity = menuService.getMenuInfo(id, false, false);
        return success(entity);
    }

    /**
     * 添加一个 <strong>菜单信息</strong>
     *
     * @param entity 修改后的信息
     */
    @PreAuthorize("hasAnyRole('ADMIN', 'MENU') or hasAnyAuthority('menu.add')")
    @PostMapping
    public Object add(@RequestBody MenuEntity entity) {
        menuService.saveMenu(entity);

        HttpHeaders headers = new HttpHeaders();
        headers.add(HttpHeaders.LOCATION, String.valueOf(entity.getId()));
        return new ResponseEntity<>(headers, HttpStatus.CREATED);
    }

    /**
     * 修改一个 <strong>菜单信息</strong>
     *
     * @param id     主键ID
     * @param entity 修改后的信息
     */
    @PreAuthorize("hasAnyRole('ADMIN', 'MENU') or hasAnyAuthority('menu.update')")
    @PutMapping("{id}")
    public Object update(@PathVariable String id, @RequestBody MenuEntity entity) {
        entity.setId(id);
        menuService.updateMenu(entity);
        return new ResponseEntity<>(HttpStatus.OK);
    }

    /**
     * 修改一个 <strong>菜单信息</strong> 的父级和排序，维持与前端表现一致
     *
     * @param entities 信息列表
     */
    @PreAuthorize("hasAnyRole('ADMIN', 'MENU') or hasAnyAuthority('menu.update', 'menu.add')")
    @PutMapping("maintain")
    public Object maintainParentAndSorted(@RequestBody List<MenuEntity> entities) {
        menuService.maintainParentAndSorted(entities);
        return new ResponseEntity<>(HttpStatus.OK);
    }

    /**
     * 删除一个 <strong>菜单信息</strong>
     *
     * @param id 主键ID
     */
    @PreAuthorize("hasAnyRole('ADMIN', 'MENU') or hasAnyAuthority('menu.delete')")
    @DeleteMapping("{id}")
    public Object delete(@PathVariable String id) {
        menuService.deleteMenu(id);
        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }

    /**
     * 删除多个 <strong>菜单信息</strong>
     *
     * @param ids 主键ID列表
     */
    @PreAuthorize("hasAnyRole('ADMIN', 'MENU') or hasAnyAuthority('menu.delete')")
    @DeleteMapping
    public Object deleteIds(@RequestBody List<String> ids) {
        menuService.deleteMenu(ids);
        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }

    /**
     * 获取某个菜单的角色列表
     *
     * @param menuId 菜单ID
     */
    @PreAuthorize("hasAnyRole('ADMIN','MENU') or hasAnyAuthority('menu.config-roles')")
    @GetMapping("{menuId}/roles")
    public Object getMenuRoles(@PathVariable String menuId) {
        return success(roleService.getMenuRoles(menuId));
    }

    /**
     * 设置某个菜单的角色列表（给菜单分配角色）
     *
     * @param menuId 菜单ID
     */
    @PreAuthorize("hasAnyRole('ADMIN','MENU') or hasAnyAuthority('menu.config-roles')")
    @PutMapping("{menuId}/roles")
    public Object setMenuRoles(@PathVariable String menuId, @RequestBody List<RoleEntity> roleEntities) {
        MenuEntity entity = new MenuEntity();
        entity.setId(menuId);
        entity.setRoles(roleEntities);
        menuService.setMenuRoles(entity);

        return new ResponseEntity<>(HttpStatus.OK);
    }

    /**
     * 获取某个菜单的权限列表
     *
     * @param menuId 菜单ID
     */
    @PreAuthorize("hasAnyRole('ADMIN','MENU') or hasAnyAuthority('menu.config-permissions')")
    @GetMapping("{menuId}/permissions")
    public Object getMenuPermissions(@PathVariable String menuId) {
        return success(permissionService.getMenuPermissions(menuId));
    }

    /**
     * 设置某个菜单的权限列表（给菜单分配权限）
     *
     * @param menuId 菜单ID
     */
    @PreAuthorize("hasAnyRole('ADMIN','MENU') or hasAnyAuthority('menu.config-permissions')")
    @PutMapping("{menuId}/permissions")
    public Object setMenuPermissions(@PathVariable String menuId, @RequestBody List<PermissionEntity> permissionEntities) {
        MenuEntity entity = new MenuEntity();
        entity.setId(menuId);
        entity.setPermissions(permissionEntities);
        menuService.setMenuPermissions(entity);

        return new ResponseEntity<>(HttpStatus.OK);
    }

    /**
     * 构建查询条件内容
     *
     * @param entity 实体对象
     * @return lambda query chain wrapper
     */
    private LambdaQueryChainWrapper<MenuEntity> buildLambdaQuery(MenuEntity entity) {
        LambdaQueryChainWrapper<MenuEntity> lambdaQuery = menuService.lambdaQuery();
        lambdaQuery.eq(StringUtils.isNotBlank(entity.getId()), MenuEntity::getId, entity.getId());
        lambdaQuery.like(StringUtils.isNotBlank(entity.getPid()), MenuEntity::getPid, entity.getPid());
        lambdaQuery.like(StringUtils.isNotBlank(entity.getTitle()), MenuEntity::getTitle, entity.getTitle());
        lambdaQuery.like(StringUtils.isNotBlank(entity.getPath()), MenuEntity::getPath, entity.getPath());
        lambdaQuery.like(StringUtils.isNotBlank(entity.getRedirect()), MenuEntity::getRedirect, entity.getRedirect());
        lambdaQuery.eq(entity.getType() != null, MenuEntity::getType, entity.getType());
        lambdaQuery.like(StringUtils.isNotBlank(entity.getComponentName()), MenuEntity::getComponentName, entity.getComponentName());
        lambdaQuery.like(StringUtils.isNotBlank(entity.getComponentPath()), MenuEntity::getComponentPath, entity.getComponentPath());
        lambdaQuery.like(StringUtils.isNotBlank(entity.getIcon()), MenuEntity::getIcon, entity.getIcon());
        lambdaQuery.eq(entity.getIsShow() != null, MenuEntity::getIsShow, entity.getIsShow());
        lambdaQuery.eq(entity.getIsShowChildren() != null, MenuEntity::getIsShowChildren, entity.getIsShowChildren());
        lambdaQuery.eq(entity.getIsKeepAlive() != null, MenuEntity::getIsKeepAlive, entity.getIsKeepAlive());
        lambdaQuery.eq(entity.getSorted() != null, MenuEntity::getSorted, entity.getSorted());
        lambdaQuery.eq(entity.getSite() != null, MenuEntity::getSite, entity.getSite());
        return lambdaQuery;
    }

}
